<?php

/**
 * Database Migration Scripts
 * @package Nina
 * @author John Main <john@thedaddy.org>
 */
class Nina_Migration
{
	protected $reg;
	protected $db;
	protected $core_migrations_path;
	protected $site_migrations_path;

	/**
	 * Constructor
	 */
	public function __construct() {
		$this->reg = Nina_Registry::getInstance();
		$this->db = $this->reg->get("database.master.instance");

		$this->core_migrations_path = $this->reg->get("database.migration_folders.core") ? $this->reg->get("database.migration_folders.core") : 'data/migrations/core';
		$this->site_migrations_path = $this->reg->get("database.migration_folders.site") ? $this->reg->get("database.migration_folders.site") : 'data/migrations/site';
	}

	/**
	* Generate a core migration file
	* @param string $title The title of the migration
	*/
	public function generateCoreMigration($title) {
		return $this->generateMigration($title, true);
	}

	/**
	* Generate a site migration file
	* @param string $title The title of the migration
	*/
	public function generateSiteMigration($title) {
		return $this->generateMigration($title, false);
	}

	/**
	* Generate an SQL migration file
	* @param string $title The title of the migration
	* @param boolean [$core=true] Core or site level
	*/
	public function generateMigration($title, $core = true) {

		$title = preg_replace("/[^a-zA-Z0-9_]/", '_', strtolower($title));

		$file_name =  date('YmdHis') . '_' . $title . '.sql';

		$migrations_dir = ($core) ? $this->core_migrations_path : $this->site_migrations_path;

		if(is_dir($migrations_dir) || @mkdir($migrations_dir)) {
			if(touch($migrations_dir.DIRECTORY_SEPARATOR.$file_name))
				echo "Migration created: $file_name\n";
			else
				echo "Cannot write to migrations directory: $migrations_dir\n";
		}
		else
			echo "Could not create migrations directory\n";
	}

	public function migrate() {
		$this->migrationHelper(true);
		$this->migrationHelper(false);
	}

	public function migrateCore() {
		$this->migrationHelper(true);
	}

	public function migrateSite() {
		$this->migrationHelper(false);
	}

	/**
	* Runs the migrations
	* @param boolean $core Declares if this is a core migration
	*/
	protected function migrationHelper($core) {

		//Get list of already run migrations from the database
		$already_run = Nina_Model_Migration::get($core);

		//Gets the path to the migrations folder
		$path = ($core) ? $this->core_migrations_path : $this->site_migrations_path;

		if(is_dir($path)) {

			//Go through each file
			foreach(scandir($path) as $migration) {

				//Pull out the migration number and class name
				if(preg_match('/^([0-9]+)\_([a-zA-Z0-9\_]+)(\.sql|)?$/', $migration, $matches)) {
					$migration_number = $matches[1];
					$migration_name = $matches[2];

					if(!in_array($migration_number, $already_run)) {
						$mark_migration = true;

						echo 'Migrating: ' . $migration_name . "\n";

						try {
							$sql = file_get_contents($path.DIRECTORY_SEPARATOR.$migration);
							$queries = preg_split("/;+(?=([^'|^\\\']*['|\\\'][^'|^\\\']*['|\\\'])*[^'|^\\\']*[^'|^\\\']$)/", $sql);
							foreach ($queries as $query){
								if (trim($query))
									$this->db->query($query);
							}
						}
						catch (Exception $e) {
							echo $e->getMessage()."\n";

							if(!defined("STDIN")) {
								define("STDIN", fopen('php://stdin', 'r'));
							}

							echo "Error Encountered with migration $migration\n";
							echo "Continue [C], Mark migration as completed and continue [M] or Abort[A]?\n";

							$continue = trim(fread(STDIN, 100));

							//Abort - die right away
							if(strtolower($continue) != 'c' && strtolower($continue) != 'm')
								die();
							//Carry on but leave this migration outstanding
							elseif(strtolower($continue) == 'c')
								$mark_migration = false;
						}

						//Add this migration to the database
						if($mark_migration) {
							$migration = new Nina_Model_Migration();
							$migration->setIndex($migration_number);
							$migration->setCore($core);
							$migration->save();
						}
					}
				}
			}
		}
	}

	/**
	* Generate the models
	*/
	public function generateModels() {
		$model_root = (defined('MODEL_ROOT')) ? MODEL_ROOT : APP_ROOT . DIRECTORY_SEPARATOR . 'models';

		if(is_dir($model_root) || @mkdir($model_root)) {
			if(is_writable($model_root)) {
				$tables = $this->db->getTables();

				foreach($tables as $table) {
					$class_name = Nina_Utils::tableToClassName($table);

					$fields = $this->db->getFields($table);

					if(in_array('id', $fields)) {
						$table_data = "	// Table Name\n	const TABLE_NAME = '" . $table . "';\n\n	//Primary Key\n	const ID = '" . $table . ".id';\n\n	// Table Fields\n";

						$foreign_keys = array();

						foreach($this->db->getFields($table) as $field) {
							if($field != 'id') {
								$table_data .= "	const " . strtoupper($field) ." = '" . $table . '.' . $field . "';\n";

								if(substr($field, -3) == '_id') {
									$foreign_table = Nina_Inflect::pluralizeUnderscore(substr($field, 0, -3));

									if(in_array($foreign_table, $tables)) {
										$foreign_keys[] = 'self::' . strtoupper($field) ." => '" . Nina_Utils::tableToClassName($foreign_table) . "'";
									}
								}
							}
						}

						if($foreign_keys) {
							$foreign_key_data = "\n" . '	protected $foreignKeys = array(' . "\n		";
							$foreign_key_data .= implode(",\n		", $foreign_keys);
							$foreign_key_data .= "\n	);\n";
						}
						else
							$foreign_key_data = '';

						$table_data = "//!! Auto-generated table information\n" . $table_data . $foreign_key_data . "	//!! End";

						if(is_file($model_root . DIRECTORY_SEPARATOR . $class_name . '.php')) {
							$file_data = file_get_contents($model_root . DIRECTORY_SEPARATOR . $class_name . '.php');

							if(preg_match('/\/\/\!\![\s\S]+\/\/\!\!(\s?end)?/i', $file_data)) {
								$file_data = preg_replace('/\/\/\!\![\s\S]+\/\/\!\!(\s?end)?/i', $table_data, $file_data);
							}
							else {
								$file_data = substr($file_data, 0, strpos($file_data, '{')+1) . "\n\n	" . $table_data . "\n" . substr($file_data, strpos($file_data, '{')+1);
							}
						}
						else {
							if(is_file(APP_ROOT . DIRECTORY_SEPARATOR . 'Model' . DIRECTORY_SEPARATOR . $class_name . '.php'))
								$extends = 'Nina_Model_' . $class_name;
							else
								$extends = 'Nina_Model';

							$file_data = "<?php\n\n/**\n * @package [package name]\n * @author [author name] <[author email]>\n */\nclass " . $class_name . " extends " . $extends . " {\n\n	" . $table_data . "\n}";
						}

						file_put_contents($model_root . DIRECTORY_SEPARATOR . $class_name . '.php', $file_data);
					}
					else
						echo 'No primary key (ID field) found for table ' . $table . "\n";
				}
			}
			else
				echo "Model directory not writable\n";
		}
		else
			echo "Model directory not found\n";
	}
}
